# -*- coding:utf-8 -*-
"""
通过supervisord部署nginx、uwsgi
"""
__author__ = "shu2015626"

import os
import time
import argparse
import subprocess
import psutil

PROJECT_FLAG = "PyTimedTasks"
HOME_PATH = "/home/anand"

# 需要事先准备的目录
PTT_RUNTIME_DIR = f"{HOME_PATH}/var/run/PyTimedTasks/"
PTT_LOGFILE_DIR4PROJECT = f"{HOME_PATH}/logs/PyTimedTasks/logfiles/"
PTT_LOGFILE_DIR4SUPERVISORD = f"{HOME_PATH}/logs/PyTimedTasks/supervisord/"
PTT_LOGFILE_DIR4UWSGI = f"{HOME_PATH}/logs/PyTimedTasks/uwsgi/"
PTT_LOGFILE_DIR4NGINX = f"{HOME_PATH}/logs/PyTimedTasks/nginx/"
PTT_LOGFILE_DIR4CELERY = f"{HOME_PATH}/logs/PyTimedTasks/celery/"
PTT_LOGFILE_DIR4FLOWER = f"{HOME_PATH}/logs/PyTimedTasks/flower/"

# supervisord相关配置
SUPERVISORD_CMDPATH = f"{HOME_PATH}/.virtualenvs/py37timed_tasks/bin/supervisord"
SUPERVISORD_CTLPATH = f"{HOME_PATH}/.virtualenvs/py37timed_tasks/bin/supervisorctl"
SUPERVISORD_PID_FILE = f"{HOME_PATH}/var/run/PyTimedTasks/supervisord_py_timed_tasks.pid"
SUPERVISORD_CONFIG = f"{HOME_PATH}/production/PyTimedTasks/deploys/supervisord/supervisord_py_timed_tasks.conf"

# 允许的业务标识，因为很多地方都用，抽出来一下
ALLOWED_FLAGS = ("all", "nginx", "uwsgi", "flower", "celerybeat", "worker_common",
                 "worker_remote", "worker_py_timed_tasks", "app_monitor")


def _mkdir(dirpath: str):
    if os.path.isdir(dirpath):
        print(f"[{PROJECT_FLAG}]目录已存在：", dirpath)
        pass
    else:
        print(f"[{PROJECT_FLAG}]新建目录：", dirpath)
        os.makedirs(dirpath)


def init(business_flag):
    print(f"[{PROJECT_FLAG}-init]参数business_flag（值为：{business_flag}）在本函数中无意义！")
    # 创建需要的目录
    _mkdir(PTT_RUNTIME_DIR)
    _mkdir(PTT_LOGFILE_DIR4PROJECT)
    _mkdir(PTT_LOGFILE_DIR4SUPERVISORD)
    _mkdir(PTT_LOGFILE_DIR4UWSGI)
    _mkdir(PTT_LOGFILE_DIR4NGINX)
    _mkdir(PTT_LOGFILE_DIR4CELERY)
    _mkdir(PTT_LOGFILE_DIR4FLOWER)
    # 初始化进程
    command_line_lst = [SUPERVISORD_CMDPATH, '-c', SUPERVISORD_CONFIG]
    print(f"[{PROJECT_FLAG}-init]正在执行supervisord初始化的命令: {' '.join(command_line_lst)}")
    completed = subprocess.run(command_line_lst,
                               stderr=subprocess.PIPE,
                               stdout=subprocess.PIPE,
                               shell=False)
    print(f'[{PROJECT_FLAG}-init]supervisord初始化的returncode:',
          completed.returncode)
    if int(completed.returncode) > 0:
        print(
            f"[{PROJECT_FLAG}-init]初始化进程失败，错误为：\n {completed.stderr.decode('utf-8')}")


def start(business_flag):
    allowed_flags = ALLOWED_FLAGS
    assert business_flag in allowed_flags, f"[{PROJECT_FLAG}]不支持的参数，目前仅支持({','.join(allowed_flags)})"

    def _start_service(cmd_line_lst):
        # 启动程序
        # Linux中，当args是个字符串时，请设置shell=True，当args是个列表的时候，shell保持默认的False。
        # windows中，args和shell参数组合比较复杂，根据命令的不同有不同的情况。建议shell设置为True。
        completed = subprocess.run(cmd_line_lst,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   shell=False)
        print(
            f"[{PROJECT_FLAG}-start]正在执行启动{business_flag}进程的命令: {' '.join(cmd_line_lst)}")
        print(f'[{PROJECT_FLAG}-start]启动{business_flag}程序中....')
        print(f'[{PROJECT_FLAG}-start]启动{business_flag}returncode:',
              completed.returncode)
        if int(completed.returncode) > 0:
            print(
                f"[{PROJECT_FLAG}-start]启动{business_flag}失败，错误为：\n {completed.stderr.decode('utf-8')}")
        print()

    if business_flag == 'all':
        start('uwsgi')
        start('nginx')
        start('celerybeat')
        start('worker_common')
        start('worker_remote')
        start('worker_py_timed_tasks')
        start('flower')
        start('app_monitor')
        return
    else:
        cmd_line_lst = [SUPERVISORD_CTLPATH, '-c',
                        SUPERVISORD_CONFIG, 'start', business_flag]
        _start_service(cmd_line_lst)


def stop(business_flag):
    allowed_flags = ALLOWED_FLAGS
    assert business_flag in allowed_flags, f"[{PROJECT_FLAG}]不支持的参数，目前仅支持({','.join(allowed_flags)})"

    def _stop_service(cmd_line_lst):
        # 关闭进程
        # Linux中，当args是个字符串是，请设置shell=True，当args是个列表的时候，shell保持默认的False。
        # windows中，args和shell参数组合比较复杂，根据命令的不同有不同的情况。建议shell设置为True。
        completed = subprocess.run(cmd_line_lst,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   shell=False)
        print(
            f"[{PROJECT_FLAG}-stop]正在执行关闭{business_flag}进程的命令: {' '.join(cmd_line_lst)}")
        print(f'[{PROJECT_FLAG}-stop]关闭{business_flag}程序中....')
        print(f'[{PROJECT_FLAG}-stop]关闭{business_flag}returncode:',
              completed.returncode)
        if int(completed.returncode) > 0:
            print(
                f"[{PROJECT_FLAG}-stop]关闭{business_flag}失败，错误为：\n {completed.stderr.decode('utf-8')}")
        print()

    if business_flag == 'all':
        stop('app_monitor')
        stop('nginx')
        stop('uwsgi')
        stop('flower')
        stop('celerybeat')
        stop('worker_common')
        stop('worker_remote')
        stop('worker_py_timed_tasks')
        return
    else:
        command_line_lst = [SUPERVISORD_CTLPATH, '-c',
                            SUPERVISORD_CONFIG, 'stop', business_flag]
        _stop_service(command_line_lst)


def restart(business_flag):
    allowed_flags = ALLOWED_FLAGS
    assert business_flag in allowed_flags, f"[{PROJECT_FLAG}]不支持的参数，目前仅支持({','.join(allowed_flags)})"

    def _restart_service(cmd_line_lst):
        # 关闭进程
        # Linux中，当args是个字符串是，请设置shell=True，当args是个列表的时候，shell保持默认的False。
        # windows中，args和shell参数组合比较复杂，根据命令的不同有不同的情况。建议shell设置为True。
        completed = subprocess.run(cmd_line_lst,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   shell=False)
        print(
            f"[{PROJECT_FLAG}-restart]正在执行重启{business_flag}进程的命令: {' '.join(cmd_line_lst)}")
        print(f'[{PROJECT_FLAG}-restart]重启{business_flag}程序中....')
        print(f'[{PROJECT_FLAG}-restart]重启{business_flag}returncode:',
              completed.returncode)
        if int(completed.returncode) > 0:
            print(
                f"[{PROJECT_FLAG}-restart]重启{business_flag}失败，错误为：\n {completed.stderr.decode('utf-8')}")
        print()

    if business_flag == 'all':
        restart('uwsgi')
        restart('nginx')
        restart('celerybeat')
        restart('worker_common')
        restart('worker_remote')
        restart('worker_py_timed_tasks')
        restart('flower')
        restart('app_monitor')
        return
    else:
        command_line_lst = [SUPERVISORD_CTLPATH, '-c',
                            SUPERVISORD_CONFIG, 'restart', business_flag]
        _restart_service(command_line_lst)


def finish(business_flag):
    print(f"[{PROJECT_FLAG}-finish]参数business_flag（值为：{business_flag}）在本函数中无意义！")
    # 初始化进程
    pid_file = SUPERVISORD_PID_FILE
    if not os.path.isfile(pid_file):
        print(f"[{PROJECT_FLAG}-finish]后台程序不存在！")
        return
    else:
        with open(pid_file, 'rt') as f:
            pid = int(str(f.readline().replace("\n", "")).strip())
        if psutil.pid_exists(pid):
            print(f"[{PROJECT_FLAG}-finish]supervisord的pid为: {pid}")
        else:
            print(
                f"[{PROJECT_FLAG}-finish]pid为:{pid}的supervisord进程不存在，只需清理该文件即可，文件路径为{pid_file}")
            os.remove(pid_file)
            return

        # 此处，必须直接关闭主进程，不能先关闭子进程。因为这里要关闭的是supervisor主进程，只关闭子进程，是会自动重启的！
        command_line_list = ["kill", "-s", "SIGTERM", str(pid)]
        print(f"[{PROJECT_FLAG}-finish]正在关闭supervisord进程，命令是：%s" %
              " ".join(command_line_list))
        subprocess.run(command_line_list, shell=False)

    while True:
        if not psutil.pid_exists(pid):
            print(f"[{PROJECT_FLAG}-finish]supervisord进程已关闭!")
            break
        else:
            print(f"[{PROJECT_FLAG}-finish]请稍等，supervisord进程关闭中...")
            time.sleep(5)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Py定时任务(py_timed_tasks)应用")
    parser.add_argument(
        dest='action',
        choices=('init', 'start', 'stop', 'restart', 'finish'),
        help='需要对程序进行的操作：初始化/开启/关闭/重启/终结',
    )

    parser.add_argument(
        dest='business',
        choices=('all', 'nginx', 'uwsgi', 'flower', 'celerybeat',
                 'worker_common', 'worker_remote', 'worker_py_timed_tasks'),
        help='需要进行的业务',
    )
    args = parser.parse_args()

    action_flag = args.action
    business_flag = args.business
    if action_flag == "init":
        init(business_flag)
    elif action_flag == 'start':
        start(business_flag)
    elif action_flag == 'stop':
        stop(business_flag)
    elif action_flag == 'restart':
        restart(business_flag)
    elif action_flag == 'finish':
        finish(business_flag)
    else:
        print("只支持：init,start,stop,restart,finish操作，请检查!")
